package br.org.acessobrasil.portal.filtro;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import br.org.acessobrasil.portal.Gerenciador;
import br.org.acessobrasil.portal.modelo.Pagina;
import br.org.acessobrasil.portal.persistencia.PaginaDaoSpring;
import br.org.acessobrasil.portal.util.HtmlUtils;
import br.org.acessobrasil.portal.util.StringUtils;

public class Filtro implements Filter{
	private static Logger logger = Logger.getLogger(Filtro.class);
	private static final String PG_PREFIXO="seo-";
	private static boolean usarNomeReal = true;
	private static PaginaDaoSpring paginaDao;
	private static final Pattern patAlterarLinks = Pattern.compile("href=(\"index.action|\")\\?nu_pagina=(\\d*?)(#[^\"]*\"|\")");
	/**
	 * Define o maximo de caracteres dentro de um titulo de um link 
	 * tratado para ficar mais inteligivel
	 */
	private static final int MAX_CHAR_TAG_A_HREF_TITLE = 255;
	
	/**
	 * Inicio para dar o stop
	 */
	private static final int INI_STOP_CHAR_A_HREF_TITLE = 40;
	/**
	 * Eg.: /CMS08
	 */
	private String nomeDaAplicacao;
	private static boolean usarSEO = false;
	public void init(FilterConfig filterConfig) throws ServletException {
		String seo = filterConfig.getInitParameter("SEO");
		if(seo!=null && seo.equals("true")){
			usarSEO = true;
			logger.info("Usando modificacoes para SEO.");
		}
		nomeDaAplicacao = filterConfig.getServletContext().getContextPath();
		logger.debug("nomeDaAplicacao = " + nomeDaAplicacao);
	}
	
	/**
	 * Conta o numero de ocorrencias de um caractere dentro de uma string
	 * @param c char a ser procurado
	 * @param str string onde o char sera procurado
	 * @return numero de ocorrencias
	 */
	private int contarQtdChar(char c,String str){
		int retorno=0;
		int tot = str.length();
		for(int i=0;i<tot;i++){
			if(str.charAt(i)==c) retorno++;
		}
		return retorno;
	}
	
	private boolean tratarPaginaPorUri(HttpServletRequest request,HttpServletResponse response) throws IOException{
		Pagina pagina = null;
		//String retorno = null;
		if(paginaDao!=null){
			String uri = request.getRequestURI();
			if(uri.startsWith(nomeDaAplicacao+'/')){//retiramos o nome da aplicacao para fazer a busca
				uri = uri.substring(nomeDaAplicacao.length());
			}
			pagina = paginaDao.buscarPorUri(uri);
			if(pagina!=null){
				Gerenciador gerenciador = new Gerenciador(request,response);
				String html = alterarLinks(gerenciador.gerarPaginaHtml(pagina));
				URL url = new URL(request.getRequestURL().toString());
				String baseHref = "";
				if(url.getPort()!=80){
					baseHref = url.getProtocol()+"://"+url.getHost()+':'+url.getPort()+nomeDaAplicacao+'/';
				}else{
					baseHref = url.getProtocol()+"://"+url.getHost()+nomeDaAplicacao+'/';
				}
				try{
					html = HtmlUtils.setBaseHref(baseHref,html);
				}catch(Exception e){
					logger.error("Erro ao atribuir o base href: " + e.getMessage(),e);
				}
				PrintWriter out = response.getWriter();
				out.write(html);
				return true;
			}
		}
		return false;
	}
	
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		long tempo = System.currentTimeMillis();
		HttpServletResponse httpResponse = (HttpServletResponse)response;
		HttpServletRequest httpRequest = (HttpServletRequest)request;
		RequestWrapper requestWrapper = null;
		String uri = httpRequest.getRequestURI();
		int fim = uri.lastIndexOf("/");
		String pgName = uri.substring(fim+1);
		logger.debug("uri = '" + uri + "' pgName=" + pgName);
		int nBarras = contarQtdChar('/',uri);
		/*
		 * nao filtrar itens fora do CMS08/
		 * eg. /CMS08/adm/qq.action nao deve ser filtrado
		 * /CMS08/qq.jsp deve ser filtrado
		 */
		if(nBarras>2){
			if(uri.startsWith(nomeDaAplicacao+"/adm/")){//se estiver no adm nao filtrar
				chain.doFilter(request, response);
			}else{//verificar se existe pagina com o uri solicitado
				boolean tratouPagina = tratarPaginaPorUri(httpRequest,httpResponse);
				if(!tratouPagina){//nao tratou pela uri da pagina "pagina.uri"
					chain.doFilter(request, response);//nao filtrar
				}
			}
		}else{
			PrintWriter out = response.getWriter();
			ResponseWrapper	wrapper = new ResponseWrapper(httpResponse);
			if(usarSEO && (pgName.startsWith(PG_PREFIXO) || pgName.equals("index.action"))){
				gerarPagina(httpRequest, requestWrapper, response, httpResponse, wrapper, fim, uri, out);
			}else if(usarSEO && pgName.equals("")){
				//Pagina principal
				response.setContentType("text/html");
				Gerenciador gerenciador = new Gerenciador(httpRequest,httpResponse);
				out = httpResponse.getWriter();
				String html = alterarLinks(gerenciador.gerarPaginaHtml());
				out.write(html);
			}else if(usarSEO && pgName.startsWith("arq-")){
				response.setContentType("text/html");
				uri = uri.substring(fim+5,uri.lastIndexOf('.'));
				logger.debug("uri = " + uri);
				String par[]=uri.split("-");
				Long nuConteudo = Long.parseLong(par[par.length-1]);
				Gerenciador gerenciador = new Gerenciador(httpRequest,httpResponse);
				out = httpResponse.getWriter();
				String html = alterarLinks(gerenciador.gerarPaginaHtmlArquivo(nuConteudo,par[0],par[1]));
				out.write(html);
			}else if(usarSEO && pgName.startsWith("item-")){
				//item-titulo-nuPadrao-nuItem-nuPagina.htm
				response.setContentType("text/html");
				uri = uri.substring(fim+6,uri.lastIndexOf('.'));
				logger.debug("url = " + uri + " item aberto");
				String par[]=uri.split("-");
				Long nuPadraoApresentacao = Long.parseLong(par[par.length-3]);
				Long nuItemConteudo = Long.parseLong(par[par.length-2]);
				Long nuPagina = Long.parseLong(par[par.length-1]);
				Gerenciador gerenciador = new Gerenciador(httpRequest,httpResponse);
				out = httpResponse.getWriter();
				String html = gerenciador.gerarPaginaHtml(nuPagina,nuPadraoApresentacao,nuItemConteudo);
				out.write(alterarLinks(html));
			}else if(usarSEO && pgName.startsWith("arqs-")){
				response.setContentType("text/html");
				uri = uri.substring(fim+6,uri.lastIndexOf('.'));
				logger.debug("uri = " + uri);
				String par[]=uri.split("-");
				Long nuConteudo = Long.parseLong(par[par.length-1]);
				Gerenciador gerenciador = new Gerenciador(httpRequest,httpResponse);
				out = httpResponse.getWriter();
				out.write(gerenciador.gerarPaginaHtmlArquivo(nuConteudo));
			}else{
				//verificar pelo nome das paginas
				boolean tratouPagina = tratarPaginaPorUri(httpRequest,httpResponse);
				if(!tratouPagina){//nao tratou pela uri da pagina "pagina.uri"
					chain.doFilter(request, wrapper);
					String wrapperString = wrapper.toString();
					
					if (wrapperString.contains("<html")){
						// Ja possui uma tag html entao nao passar pelo gerenciador
						out.write(wrapperString);
					}else{
						//passar pelo gerenciador
						response.setContentType("text/html");
						Gerenciador gerenciador = new Gerenciador(httpRequest,httpResponse);
						gerenciador.setChainHtml(wrapperString);
						out = httpResponse.getWriter();
						out.write(gerenciador.gerarPaginaHtml());
					}
				}
			}
		}
		logger.info("tempo = " + (System.currentTimeMillis() - tempo));
	}

	/**
	 * Altera os links de index.action?nu_pagina={nuPagina} para seo-titulo-{nuPagina}.htm
	 * @param html codigo
	 * @return html com os links alterados
	 */
	private String alterarLinks(String html){
		Matcher mat = patAlterarLinks.matcher(html);
		String linksAlterados = html;
		while(mat.find()){
			String nuPagina = mat.group(2);
			String ancora = mat.group(3);
			int fim = html.indexOf("</a>", mat.start());
			int ini = html.indexOf(">", mat.start());
			if(ini!=-1 && fim!=-1){
				boolean ok = false;
				String titulo = html.substring(ini+1,fim).trim();
				if(usarNomeReal){
					try{
						Pagina pagina = paginaDao.select(Long.parseLong(nuPagina));
						if(pagina!=null && pagina.getUri()!=null){
							titulo = pagina.getUri();
							//substitui todos
							linksAlterados = linksAlterados.replace(mat.group(),"href=\""+nomeDaAplicacao+titulo+ancora);
							ok = true;
						}
					}catch(Exception e){
						logger.warn("Erro ao retornar a url real: "+e.getMessage(),e);
					}
				}
				if(!ok){
					titulo = criarLabel(titulo);
					//substitui todos
					linksAlterados = linksAlterados.replace(mat.group(),"href=\""+PG_PREFIXO+titulo+'-'+nuPagina+".htm"+ancora);
				}
				logger.debug("nuPagina "+nuPagina+"."+titulo);
				
			}
		}
		// Tratar as marcas para abrir um outro padrao
		// index.action?nu_padrao_apresentacao="+nu_padrao_apresentacao+"&amp;nu_item_conteudo="+item.getNuItemConteudo()+"&amp;nu_pagina="+conteudo.getNuPagina()
		Pattern patA2 = Pattern.compile("href=\"(index.action|)\\?nu_padrao_apresentacao=(\\d*?)(&|&amp;)nu_item_conteudo=(\\d*?)(&|&amp;)nu_pagina=(\\d*?)\"");
		mat = patA2.matcher(html);
		while(mat.find()){
			
			String nuPadraoApresentacao = mat.group(2);
			String nuItemConteudo = mat.group(4);
			String nuPagina = mat.group(6);
			int fim = html.indexOf("</a>", mat.start());
			int ini = html.indexOf(">", mat.start());
			if(ini!=-1 && fim!=-1){
				String titulo = html.substring(ini+1,fim).trim();
				titulo = criarLabel(titulo);
				logger.debug("nuPagina "+nuPagina+"."+titulo);
				//substitui todos
				//item-titulo-nuPadrao-nuItem-nuPagina.htm
				linksAlterados = linksAlterados.replace(mat.group(),"href=\"item-"+titulo+"-"+nuPadraoApresentacao+"-"+nuItemConteudo+"-"+nuPagina+".htm\"");
			}
		}
		return linksAlterados;
	}
	/**
	 * Sub rotina
	 * @param httpRequest
	 * @param requestWrapper
	 * @param response
	 * @param httpResponse
	 * @param wrapper
	 * @param fim
	 * @param uri
	 * @param out
	 * @throws IOException
	 */
	private void gerarPagina(HttpServletRequest httpRequest,RequestWrapper requestWrapper, ServletResponse response, HttpServletResponse httpResponse, ResponseWrapper wrapper,int fim,String uri,PrintWriter out) throws IOException{
		uri = uri.substring(0,uri.lastIndexOf('.'));
		requestWrapper = new RequestWrapper(httpRequest);
		logger.debug("uri = " + uri);
		String par[]=uri.split("-");
		try{
			Long nuPagina = Long.parseLong(par[par.length-1]);
			requestWrapper.setParameter("nu_pagina", nuPagina.toString());
			logger.debug("nuPagina="+nuPagina);
		}catch(Exception e){}
		response.setContentType("text/html");
		
		Gerenciador gerenciador = new Gerenciador(requestWrapper,httpResponse);
		String html = gerenciador.gerarPaginaHtml();
		String linksAlterados = alterarLinks(html);
		httpResponse.setContentLength(linksAlterados.length());
		httpResponse.setStatus(200);
		out = httpResponse.getWriter();
		out.write(linksAlterados);
	}
	/**
	 * Este m&eacute;todo cria o label a partir de um t&iacute;tulo
	 * @param titulo String com caracteres especiais
	 * @return String titulo tratado para ser parte de uma url
	 */
	public static String criarLabel(String titulo){
		//limitar o numero maximo de caracteres
		titulo = StringUtils.removeAcentos(titulo).trim();
		String retorno="";
		int estado=0;
		for(int i=0;i<titulo.length();i++){
			char c = titulo.charAt(i);
			//limitar o numero de caracteres
			if(retorno.length()>=MAX_CHAR_TAG_A_HREF_TITLE) break;
			
			if(estado==0){
				if(Character.isLetter(c) || Character.isDigit(c)){
					retorno+=c;
				}else if(c==' ' || c=='\n' || c=='\t' || c=='\r'){
					if(retorno.length()>=INI_STOP_CHAR_A_HREF_TITLE){
						break;
					}else{
						retorno+='-';
						estado=4;//aguarda um novo char valido
					}
				}else if(c=='&'){
					estado=1;
				}else if(c=='<'){
					estado=3;//aguarda um fecha tag
				}
			}else if(c=='<'){//abre alguma tag
				estado=3;
			}else if(estado==1){//abre um entity eg. &amp;
				if(Character.isLetter(c) || Character.isDigit(c)){
					retorno+=c;
				}
				estado=2;
			}else if(estado==2){//fecha um entity eg. &amp;
				if(c==';') estado = 0;
			}else if(estado==3){//fecha alguma tag
				//procurar um alt
				int alt=-1;
				for(;i<titulo.length();){
					c = titulo.charAt(i);
					if(c=='>'){//sai por fechar a tag
						estado = 0;
						break;
					}else if(alt==-1 && (c==' ' || c=='\n' || c=='\t' || c=='\r')){
						alt=0;
					}else if(alt==0 && c=='a'){
						alt=1;
					}else if(alt==1 && c=='l'){
						alt=2;
					}else if(alt==2 && c=='t'){
						alt=3;
					}else if(alt==3 && c=='='){
						alt=4;
					}else if(alt==4 && c=='"'){
						alt=5;
						i++;//pular esta primeira aspas
						//copiar ate fechar as aspas
						String altConteudo="";
						for(;i<titulo.length();){
							c = titulo.charAt(i);
							if(c=='"'){
								break;
							}
							altConteudo+=c;
							i++;
						}
						retorno+=criarLabel(altConteudo);
					}else{
						alt=-1;
					}
					i++;
				}
			}else if(estado==4){
				if(Character.isLetter(c) || Character.isDigit(c)){
					retorno+=c;//char valido
					estado=0;
				}
			}
		}
		logger.debug("titulo["+titulo + "]->[" + retorno + "]");
		return retorno.replaceAll("--*","-").toLowerCase();
	}
	
	public PaginaDaoSpring getPaginaDao() {
		return paginaDao;
	}
	public void setPaginaDao(PaginaDaoSpring paginaDao) {
		Filtro.paginaDao = paginaDao;
	}
	
	public void destroy() {
		
	}
}
